﻿using OAuth2Authorizer.Serialization;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
using System.Linq;
using System.Net;
using System.Runtime.Serialization.Json;
using System.Text;
using System.Web;

namespace OAuth2Authorizer.Framework
{
    // Primary responsibility is to handle the third leg of oAuth2.
    internal abstract class AuthorizerBase : IAuthorizer
    {
        protected string _tokenUrl;
        protected bool _shouldGet = false;

        internal AuthorizerBase(string tokenUrl)
        {
            _tokenUrl = tokenUrl;
        }

        private string GetQueryString(NameValueCollection parameters)
        {
            var serializer = new NameValueCollectionSerializer();
            return serializer.GetFormEncodedString(parameters);
        }

        private AccessTokenResponse  GetAccessTokenInternal(string url, NameValueCollection parameters)
        {
            AccessTokenResponse responseObject = default(AccessTokenResponse);
            var request = (HttpWebRequest)WebRequest.Create(url);
            if (parameters != null && parameters.Count > 0)
            {
                string paramString = GetQueryString(parameters);
                byte[] paramByteBuffer = Encoding.UTF8.GetBytes(paramString);
                request.ContentLength = paramByteBuffer.Length;
                request.ContentType = "application/x-www-form-urlencoded";
                request.Method = "POST";
                request.Accept = "application/json";
                using (var writer = new BinaryWriter(request.GetRequestStream()))
                {
                    writer.Write(paramByteBuffer);
                }
            }

            using (var response = request.GetResponse())
            {
                responseObject = GetResponseObject(response.GetResponseStream(), response.ContentType); 
            }
            return responseObject;
        }

        private AccessTokenResponse GetAccessTokenInternalWithGet(string url, NameValueCollection parameters)
        {
            AccessTokenResponse responseObject = default(AccessTokenResponse);
            if (parameters != null && parameters.Count > 0)
            {
                url += "?" + GetQueryString(parameters);
            }
            var request = (HttpWebRequest)WebRequest.Create(url);
            using (var response = request.GetResponse())
            {
                responseObject = GetResponseObject(response.GetResponseStream(), response.ContentType);
            }
            return responseObject;
        }


        protected abstract string GetAuthorizeUrlInternal(string appKey, string redirectUrl, string state);
        protected abstract NameValueCollection GetTokenRequestParameters(string appKey, string appSecret, string redirectUrl, string code);
        protected abstract AccessTokenResponse GetResponseObject(Stream stream, string contentType);

        public string GetAuthorizeUrl(string appKey, string redirectUrl, string state, NameValueCollection parameters = null)
        {
            var url = GetAuthorizeUrlInternal(appKey, redirectUrl, state);
            var serializer = new NameValueCollectionSerializer();
            var parameterString = serializer.GetFormEncodedString(parameters);
            if(parameterString.Length>0)
                url += "&" + parameterString;
            return url;
        }

        public AccessTokenResponse GetAccessToken(string appKey, string appSecret, string redirectUrl, string code, NameValueCollection parameters = null)
        {
            var requestParameters = GetTokenRequestParameters(appKey, appSecret, redirectUrl, code);
            if (parameters != null && parameters.Count > 0)
                foreach (var key in parameters.AllKeys)
                    requestParameters.Add(key, parameters[key]);
            return _shouldGet ? GetAccessTokenInternalWithGet(_tokenUrl, requestParameters) : GetAccessTokenInternal(_tokenUrl, requestParameters);
        }
    }
}
